﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using tiDE.API.Utility;
using System.IO;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
using tiDE.Runtime;
using tiDE.CodeFolding;
using tiDE.CodeCompletion;
using ICSharpCode.TextEditor.Gui.CompletionWindow;
using ICSharpCode.TextEditor.Gui.InsightWindow;

namespace tiDE
{
    public partial class CodeWindow : DockContent
    {
        public string CodeFile { get; set; }
        public string Code
        {
            get
            {
                return TextEdit.Text;
            }
            set
            {
                TextEdit.Text = value;
            }
        }
        bool Saved = false;

        public TextEditorControl TextEdit;

        public static Dictionary<string, Type> FoldingStrategies;
        public static Dictionary<string, List<AutoReplacement>> AutomaticReplacements = new Dictionary<string, List<AutoReplacement>>();
        public static Dictionary<string, IInternalCompletionProvider> CodeCompleters = new Dictionary<string, IInternalCompletionProvider>();
        List<AutoReplacement> Replacements = new List<AutoReplacement>();
        Form MainForm;
        CodeCompletionWindow codeCompletionWindow;
        IInternalCompletionProvider CodeCompletionProvider = null;
        bool CodeCompletionDisabled = false;

        public CodeWindow(Form MainForm)
        {
        	//TODO make sure everything is initialized properly
        	//TODO Finish this constructor
        	this.MainForm = MainForm;
        	
        	InitializeComponent();
        	
        	TextEdit = new TextEditorControl();
        	TextEdit.IsIconBarVisible = true;
        	TextEdit.Dock = DockStyle.Fill;
            TextEdit.IndentStyle = ICSharpCode.TextEditor.Document.IndentStyle.Auto;
            
            TextEdit.EnableFolding = false;
            TextEdit.FileName = "Untitled";
            
            CodeCompletionDisabled = true;
            
            Controls.Add(TextEdit);
           
            //TextEdit.TextChanged += new EventHandler(Text_TextChanged);
            TextEdit.ContextMenuStrip = contextMenuStrip1;

            TextEdit.ActiveTextAreaControl.TextArea.ToolTipRequest += new ToolTipRequestEventHandler(TextArea_ToolTipRequest);

            Timer timer = new Timer();
            timer.Enabled = true;
            timer.Interval = 100;
            timer.Start();
            timer.Tick += new EventHandler(timer_Tick);
        }

        public CodeWindow(string codeFile, Form MainForm)
        {
            this.MainForm = MainForm;
            if (FoldingStrategies == null)
                FoldingStrategies = new Dictionary<string, Type>();

            InitializeComponent();

            TextEdit = new TextEditorControl();
            TextEdit.IsIconBarVisible = true;
            TextEdit.Dock = DockStyle.Fill;
            TextEdit.IndentStyle = ICSharpCode.TextEditor.Document.IndentStyle.Auto;

            string dir = RuntimeData.tiDEDirectory + "\\Highlighting";

            FileSyntaxModeProvider fsmProvider;
            fsmProvider = new FileSyntaxModeProvider(dir);
            HighlightingManager.Manager.AddSyntaxModeFileProvider(fsmProvider);

            // Find a folding strategy if present
            if (FoldingStrategies.ContainsKey(codeFile.Substring(codeFile.LastIndexOf(".") + 1)))
            {
                TextEdit.ActiveTextAreaControl.Document.FoldingManager.FoldingStrategy =
                    (IFoldingStrategy)Activator.CreateInstance(FoldingStrategies[codeFile.Substring(codeFile.LastIndexOf(".") + 1)]);
            }
            TextEdit.EnableFolding = true;

            // Find an auto-replacement strategy
            if (AutomaticReplacements.ContainsKey(codeFile.Substring(codeFile.LastIndexOf(".") + 1)))
            {
                Replacements = AutomaticReplacements[codeFile.Substring(codeFile.LastIndexOf(".") + 1)];
            }

            if (CodeCompleters.ContainsKey(codeFile.Substring(codeFile.LastIndexOf(".") + 1)))
            {
                CodeCompletionProvider = CodeCompleters[codeFile.Substring(codeFile.LastIndexOf(".") + 1)];
                TextEdit.ActiveTextAreaControl.TextArea.KeyEventHandler += new ICSharpCode.TextEditor.KeyEventHandler(TextArea_KeyEventHandler);
            }

            Controls.Add(TextEdit);
            CodeFile = codeFile;
            this.Text = DataConverter.StripDirectory(CodeFile);

            fileSystemWatcher1.Filter = DataConverter.StripDirectory(CodeFile);
            fileSystemWatcher1.Path = DataConverter.GetDirectoryFromFile(CodeFile);

            TextEdit.LoadFile(codeFile);
            TextEdit.ActiveTextAreaControl.Document.FoldingManager.UpdateFoldings("", null);
            TextEdit.TextChanged += new EventHandler(Text_TextChanged);
            TextEdit.ContextMenuStrip = contextMenuStrip1;

            TextEdit.ActiveTextAreaControl.TextArea.ToolTipRequest += new ToolTipRequestEventHandler(TextArea_ToolTipRequest);

            Timer timer = new Timer();
            timer.Enabled = true;
            timer.Interval = 100;
            timer.Start();
            timer.Tick += new EventHandler(timer_Tick);
        }

        void TextArea_ToolTipRequest(object sender, ToolTipRequestEventArgs e)
        {
            if (e.ToolTipShown || !e.InDocument)
                return;
            if (CodeCompletionProvider != null)
            {
                string toolTip = CodeCompletionProvider.GetToolTip(e.LogicalPosition,
                    TextEdit.ActiveTextAreaControl.Document);
                if (!string.IsNullOrEmpty(toolTip))
                {
                    e.ShowToolTip(toolTip);
                }
            }
        }

        bool TextArea_KeyEventHandler(char ch)
        {
            if (CodeCompletionDisabled)
                return false;

            if (codeCompletionWindow != null)
                if (codeCompletionWindow.ProcessKeyEvent(ch))
                    return true;

            foreach (char trigger in CodeCompletionProvider.CodeCompletionTriggers)
            {
                if (trigger == ch)
                {
                    codeCompletionWindow = CodeCompletionWindow.ShowCompletionWindow(
                        MainForm, TextEdit,
                        CodeFile, CodeCompletionProvider, ch);
                    if (codeCompletionWindow != null)
                    {
                        codeCompletionWindow.Closed += new EventHandler(codeCompletionWindow_Closed);
                    }
                }
            }
            return false;
        }

        void codeCompletionWindow_Closed(object sender, EventArgs e)
        {
            if (codeCompletionWindow != null)
            {
                codeCompletionWindow.Closed -= new EventHandler(codeCompletionWindow_Closed);
                codeCompletionWindow.Dispose();
                codeCompletionWindow = null;
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            if (!autoChangeWaiting)
                return;
            autoChangeWaiting = false;
            CodeCompletionDisabled = true;
            // Code helpers
            int index = TextEdit.ActiveTextAreaControl.Caret.Offset;
            foreach (AutoReplacement replacement in Replacements)
            {
                try
                {
                    string lastSection = TextEdit.Text.Substring(index - replacement.Key.Length, replacement.Key.Length);
                    if (lastSection == replacement.Key)
                    {
                        // Perform the change
                        string replacementText = TextEdit.Text;
                        replacementText = replacementText.Remove(index - replacement.Key.Length, replacement.Key.Length);
                        replacementText = replacementText.Insert(index - replacement.Key.Length, replacement.Value);
                        // Then move the caret by the difference
                        int newIndex = TextEdit.ActiveTextAreaControl.Document.PositionToOffset(
                            TextEdit.ActiveTextAreaControl.Caret.Position) - (replacement.Key.Length - replacement.Value.Length);
                        TextEdit.Text = replacementText;
                        TextEdit.ActiveTextAreaControl.Caret.Position = TextEdit.ActiveTextAreaControl.Document.OffsetToPosition(newIndex);
                    }
                }
                catch (Exception ex)
                { 
                }
            }
            CodeCompletionDisabled = false;
        }

        bool autoChangeWaiting = false;

        void Text_TextChanged(object sender, EventArgs e)
        {
            this.Text = DataConverter.StripDirectory(CodeFile) + "*";
            autoChangeWaiting = true;
            TextEdit.ActiveTextAreaControl.Document.FoldingManager.UpdateFoldings("", null);
        }

        public void Save()
        {
            if (Saved)
                return;

            //This was added in to deal with untitled files
            if (CodeFile == null) //Then ask for a name for the untitled file
            {
            	//TODO: improve file dialog (ex. extentions, etc.)
            	FileDialog fDialog = new SaveFileDialog();
            	fDialog.ShowDialog();
            	if (fDialog.FileName == "")
            	{
            		Text = "Untited*";
            		return;
            	}
            	else
            	{
            		CodeFile = fDialog.FileName;
            	}
            }

            Saved = true;
            this.Text = DataConverter.StripDirectory(CodeFile);
            try
            {
                StreamWriter writer = new StreamWriter(CodeFile);
                writer.Write(TextEdit.Text);
                writer.Close();
            }
            catch { }
        }

        int numTimesFired = 1;

        private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
        {
            if (numTimesFired == 1)
            {
                numTimesFired--;
                return;
            }
            if (numTimesFired == 0)
                numTimesFired = 1;
            if (Saved)
            {
                Saved = false;
                return;
            }
            if (MessageBox.Show(DataConverter.StripDirectory(CodeFile) + " has been modified outside of the editor.\nReload it?",
                "File Change", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                TextEdit.Text = "";
                StreamReader reader = new StreamReader(CodeFile);
                TextEdit.Text = reader.ReadToEnd();
                reader.Close();
            }
        }

        private void CodeWindow_Load(object sender, EventArgs e)
        {

        }

        private void collapseAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (FoldMarker marker in TextEdit.ActiveTextAreaControl.Document.FoldingManager.FoldMarker)
                marker.IsFolded = true;
            TextEdit.ActiveTextAreaControl.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty);
        }

        private void expandAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (FoldMarker marker in TextEdit.ActiveTextAreaControl.Document.FoldingManager.FoldMarker)
                marker.IsFolded = false;
            TextEdit.ActiveTextAreaControl.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty);
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!TextEdit.ActiveTextAreaControl.SelectionManager.HasSomethingSelected)
                return;
            Clipboard.SetText(TextEdit.ActiveTextAreaControl.SelectionManager.SelectedText);
        }

        private void cutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!TextEdit.ActiveTextAreaControl.SelectionManager.HasSomethingSelected)
                return;
            Clipboard.SetText(TextEdit.ActiveTextAreaControl.SelectionManager.SelectedText);
            TextEdit.ActiveTextAreaControl.SelectionManager.RemoveSelectedText();
        }

        private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (Clipboard.ContainsText())
                TextEdit.ActiveTextAreaControl.Document.Insert(TextEdit.ActiveTextAreaControl.Caret.Offset,
                    Clipboard.GetText());
        }

        private void disableToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (disableToolStripMenuItem.Text == "Disable")
            {
                TextEdit.EnableFolding = false;
                disableToolStripMenuItem.Text = "Enable";
            }
            else
            {
                TextEdit.EnableFolding = true;
                disableToolStripMenuItem.Text = "Disable";
            }
        }
    }

    public class AutoReplacement
    {
        /// <summary>
        /// The string to be replaced upon being typed in
        /// </summary>
        public string Key { get; set; }
        /// <summary>
        /// The value to replace it with
        /// </summary>
        public string Value { get; set; }

        public AutoReplacement(string Key, string Value)
        {
            this.Key = Key;
            this.Value = Value;
        }
    }
}
